#include "ghosts.h"
#include "pac.h"
// Призраците имат нужда от малко информация, за да се ориентират правилно.
extern char map[COLS][ROWS];
extern Pacman * Pac;
extern int dotsLeft;

// В милисекунди
int Ghost::last_scared_at = 0;
int Ghost::scared_time = 10000;
int Ghost::blink_time = 2000;

// enum ghost_states {ALIVE, SCARED, EATEN, SCATTER, ELROY1, ELROY2};
// PacSpeed = 100;
// TODO: Calibrate for maximum enjoyment :)
int Ghost::speed[] = {98, 49, 80, 98, 120, 150};

// Колко точки трябва да са останали на екрана, за да се появи Елрой.
int Blinky::elroy_threshold_1 = 100;
int Blinky::elroy_threshold_2 = 50;

Ghost::Ghost(int _x, int _y, Spritesheet * sheet, int _spriteset):
Actor(_x, _y, sheet, _spriteset),default_spriteset(_spriteset)
{
    state = NORMAL;
}

/*
 * Проверява дали призракът и Пакман се застъпват.
 * Извършва съответните действия
 * (ако призракът е уплашен, той умира, ако не е - Пакман умира)
 * */
int Ghost::collision()
{
    if(get_tile() == Pac->get_tile())
    {
        if(state == SCARED)
        {
            become_eaten();
            return GHOST_EATEN;
        }
        else if(state == NORMAL)
            return PAC_EATEN;
    }
    return NO_COLLISION;
}

/*
 * Призраците нямат анимация за direction=NONE, защото никога не стоят на едно място.
 * Това е отразено тук.
 * */
void Ghost::draw(SDL_Surface * screen)
{
    if(dir==NONE)
        dir = UP;
    // Ако призракът е уплашен, но си възвръща съзнанието, накарай го да примигва.
    if(state == SCARED)
    {
        int now = SDL_GetTicks();
        int scared_time_left = Ghost::scared_time - (now - Ghost::last_scared_at);
        if( scared_time_left < Ghost::blink_time)
        {
            // Забележка. Тези не са реално бели :)
            if(scared_time_left % 1000 > 500)
                current_spriteset = WHITE;
            else
                current_spriteset = BLUE;
        }
    }
    Actor::draw(screen);
}

void Ghost::set_state(int _state)
{
    state = _state;
}

int Ghost::get_speed()
{
    return speed[state];
}

void Ghost::scare()
{
    if(state != SCARED && state != EATEN)
    {
        set_spriteset(BLUE);
        // Когато е уплашен, призракът завива наобратно.
        set_direction(3 - dir);
        set_state(SCARED);
    }
}

void Ghost::unscare()
{
    if(state == SCARED)
    {
        set_spriteset(default_spriteset);
        set_state(NORMAL);
    }
}

void Ghost::become_eaten()
{
    if(state == SCARED)
    {
        set_spriteset(EYES);
        set_state(EATEN);
    }
}

void Ghost::become_alive()
{
    if(state == EATEN)
    {
        set_spriteset(default_spriteset);
        set_state(NORMAL);
    }
}

void Ghost::roadblock_callback()
{
    //dir = NONE;
}


void Ghost::move()
{
    Tile curTile = get_tile();
    // Ако призракът е бил изяден, но е успял да се върне вкъщи, да дойде тук.
    if(curTile == home_tile() && state == EATEN)
    {
        become_alive();
    }
    
    // Ако призракът е на кръстовище.
    if(x % TILE_SIZE == 0 && y % TILE_SIZE == 0)
    {
        // Разглежда 3 посоки(без наобратно) и отива в най-удобната.
        // Това е оригиналният алгоритъм в Пак-ман.
        
        // TODO: Тази част да стане по-красива.
        int _x,_y;
        int min_dist = 99999, dist;
        Tile targTile = target_tile();
        for(int newDir=0;newDir<4;newDir++)
        {
            /* От потенциалните кандидати, взема се най-близкия до target_tile
             * Има няколко случая, в които ход на призрак не е валиден:
             * Когато се връща наобратно(това може да стане само ако е уплашен)
             * Когато се опитва да влезе в базата без да е изяден.
             * Когато се опитва да излезе от базата твърде рано.
             * */
            bool turning_back = (dir + newDir == 3);
            if(!turning_back)
            {
                _x = curTile.x + direction[newDir][0];
                _y = curTile.y + direction[newDir][1];
                // В базата могат да влизат само мъртви призраци
                // Всичко друго може само да излиза
                bool returning_to_base = (state != EATEN && map[_x][_y] == BASE && newDir == DOWN);
                bool exiting_base_too_early = Pac->get_dots_eaten() < this->min_dots_to_enter_play;
                if(map[_x][_y] != WALL && !returning_to_base && !exiting_base_too_early)
                {
                    dist = (targTile.x-_x)*(targTile.x-_x) + (targTile.y-_y)*(targTile.y-_y);
                    if(dist < min_dist)
                    {
                        desired_dir = newDir;
                        min_dist = dist;
                    }
                }
            }
        }
    }
    
    moves_per_second = get_speed();
    Actor::move();
}

Tile Ghost::home_tile()
{
    return Tile(COLS/2, ROWS/2 - 1);
}

Tile Ghost::outside_home_tile()
{
    return Tile(COLS/2, ROWS/2 - 5);
}

Tile Ghost::target_tile()
{
    if(state == EATEN)
        return home_tile();
    // Ако е в базата
    // TODO: Премахни хардкодването
    Tile cTile = get_tile();
    if(cTile.x >= 10 && cTile.x <= 18)
    if(cTile.y >= 16 && cTile.y <= 20)
    {
        // Ако призракът трябва да излиза вече.
        // Например ако е бил изяден и се е преродил, 
        // или ако Пакман е изял достатъчно точки от началото на рунда.
        if(Pac->get_dots_eaten() >= this->min_dots_to_enter_play)
            return outside_home_tile();
        return home_tile();
    }
    if(state == SCARED)
        return Tile(rand()%COLS, rand()%ROWS);
    // TODO: if(state == SCATTER)

    // Не сме в частен случай, призракът ще гони Пакман
    return chase_tile();
}

// INDIVIDUAL GHOSTS START HERE

Blinky::Blinky(Spritesheet * sheet, int _x, int _y):
Ghost(_x, _y, sheet, BLINKY)
{
    // Блинки винаги е на линия :)
    min_dots_to_enter_play = 0;
}

Pinky::Pinky(Spritesheet * sheet, int _x, int _y):
Ghost(_x, _y, sheet, PINKY)
{
    min_dots_to_enter_play = 12;
}

Inky::Inky(Spritesheet * sheet, int _x, int _y):
Ghost(_x, _y, sheet, INKY)
{
    min_dots_to_enter_play = 24;
}

Clyde::Clyde(Spritesheet * sheet, int _x, int _y):
Ghost(_x, _y, sheet, CLYDE)
{
    min_dots_to_enter_play = 40;
}

Tile Blinky::chase_tile()
{
    return Pac->get_tile();
}

Tile Pinky::chase_tile()
{
    Tile t = Pac->get_tile();
    t.x += direction[Pac->dir][0]*4;
    t.y += direction[Pac->dir][1]*4;
    return t;
}

/*
 * Инки използва позицията на Блинки и Пакман, за да определи къде иска да отиде.
 * Той винаги се опитва да застане симетрично на батко си, спрямо плочката 2 стъпки пред Пакман.
 * */
extern Blinky * Shadow;
Tile Inky::chase_tile()
{
    Tile tp = Pac->get_tile();
    Tile ts = Shadow->get_tile();
    tp.x += direction[Pac->dir][0]*2;
    tp.y += direction[Pac->dir][1]*2;
    tp.x += tp.x - ts.x;
    tp.y += tp.y - ts.y;
    return tp;
}

/*
 * Ако Клайд се окаже твърде близо до Пакман,
 * той тръгва да се прибира обратно вкъщи :)
 * */
Tile Clyde::chase_tile()
{
    Tile tp = Pac->get_tile();
    Tile me = get_tile();
    
    if((tp.x-me.x)*(tp.x-me.x) + (tp.y-me.y)*(tp.y-me.y) < 64)
        return scatter_tile();
    return tp;
}


/*
 * Тези нарочно е извън полето.
 * */
Tile Blinky::scatter_tile()
{
    return Tile(COLS-3,0);
}
Tile Pinky::scatter_tile()
{
    return Tile(3,0);
}

Tile Inky::scatter_tile()
{
    return Tile(COLS,ROWS);
}

Tile Clyde::scatter_tile()
{
    return Tile(0,ROWS);
}


int Blinky::get_speed()
{
    if(state == NORMAL)
    {
        if(dotsLeft < elroy_threshold_1)
            return speed[ELROY1];
        if(dotsLeft < elroy_threshold_2)
            return speed[ELROY2];
    }
    return speed[state];
}
